package com.ljy.kotlindemo

/**
 * @Author:         LiuJinYang
 * @CreateDate:     2020/8/5 16:04
 *
 * 函数与 Lambda 表达式
 */

//1. 函数声明：函数使⽤ fun 关键字声明
//2. 参数：函数参数使⽤ Pascal 表⽰法定义，即  name :  type 。参数⽤逗号隔开。每个参数必须有显式类型
//3. 默认参数：函数参数可以有默认值，当省略相应的参数时使⽤默认值。与其他语⾔相⽐，这可以减少重载数量
fun sum(a: Int = 0, b: Int = 0): Int {
    return a + b
}

private fun testFun() {
    println(sum(1, 2))
    println(sum(1))
    println(sum(b = 2))
}

//覆盖⽅法总是使⽤与基类型⽅法相同的默认参数值。 当覆盖⼀个带有默认参数值的⽅法时，必须从签名中省略默认参数值：
open class TestA {
    open fun foo(i: Int = -1) {
        println("TestA.foo($i)")
    }
}

class TestB : TestA() {
    //不能有默认值
    override fun foo(i: Int) {
        println("TestB.foo($i)")
    }
}

private fun testOverride() {
    TestA().foo()
    TestB().foo()
}

//如果⼀个默认参数在⼀个⽆默认值的参数之前，那么该默认值只能通过使⽤具名参数调⽤该函数来使⽤
fun foo(bar: Int = -1, baz: Int) {
    println("bar=$bar,baz=$baz")
}

private fun testDefault() {
    foo(baz = 10)
    foo(1, 10)
    foo(baz = 10, bar = 20)
}

//如果在默认参数之后的最后⼀个参数是 lambda 表达式，那么它既可以作为具名参数在括号内传⼊，也可以在括号外传⼊
fun foo2(bar: Int = -2, baz: Int = 2, qux: () -> Unit) {
    println("bar=$bar,baz=$baz,qux=$qux")
    qux()

}

fun testLambda() {
    foo2(1) { println("hello111") }
    foo2(qux = { println("hello222") })
    foo2 { println("hello333") }
}

//4. 具名参数
//可以在调⽤函数时使⽤具名的函数参数。当⼀个函数有⼤量的参数或默认参数时这会⾮常⽅便
//使⽤具名参数我们可以使代码更具有可读性
fun reformat(appName: String,hasNewVersion:Boolean=false,isClearData:Boolean=false,isMustDownLoad:Boolean=false){
    println("appName=$appName,hasNewVersion=$hasNewVersion,isClearData=$isClearData,isMustDownLoad=$isMustDownLoad")
}
//可以通过使⽤星号操作符将可变数量参数（vararg） 以具名形式传⼊
fun foo3(name:String,vararg strArr:String){
        println("name=$name")
    for (it in strArr){
        println(it)
    }
}

fun testName(){
    reformat("app1")
    reformat("app2",true,true,true)
    reformat(appName = "app3",hasNewVersion = true,isClearData = false,isMustDownLoad = true)
    //有时只需要修改部分参数,其余用默认值,无需重载
    reformat( "app4",hasNewVersion = true,isMustDownLoad = true)
    //当⼀个函数调⽤混⽤位置参数与具名参数时，所有位置参数都要放在第⼀个具名参数之前,上面一句可以,下面一句就不可以
//    reformat(hasNewVersion = true,"app5")

    foo3("bob111",strArr = *arrayOf("a","b","c"))
    foo3("bob222","e","f","g")
    foo3(strArr = *arrayOf("o","p","q"),name = "bob333")
}

//5. 返回 Unit 的函数
//如果⼀个函数不返回任何有⽤的值，它的返回类型是 Unit
fun foo4():Unit{
    println("hello")
    return Unit
}
//等价于：
fun foo5(){
    println("hello")
}
fun testUnit(){
    foo4()
    foo5()
}

//6. 单表达式函数
//当函数返回单个表达式时，可以省略花括号并且在 = 符号之后指定代码体即可
fun foo6(x:Int):Int{
    return x*x;
}
fun foo7(x:Int)=x*x
fun foo8(x:Int)= println(x*x)
fun testSingleLine(){
    println(foo6(5))
    println(foo7(6))
    foo8(7)
}

fun main() {
    testFun()
    testOverride()
    testDefault()
    testLambda()
    testName()
    testUnit()
    testSingleLine()
}